﻿using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Commands.CommandParameters;
using Fuse8.ContentManagementFramework.Domain.Special;
using Fuse8.ContentManagementFramework.SystemFacade.Extensions;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.DataAccess.SqlDatabase;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

namespace Fuse8.ContentManagementFramework.DataAccess.SqlAccess.Mappers
{
    internal class CmsUserInformationAllForManagementDataMapper : BaseSqlEntityDataMapper<CmsUserInformation, int>
    {
        private static BaseSqlEntityDataMapper<CmsUserInformation, int> _based = new BaseSqlEntityDataMapper<CmsUserInformation, int>();

        private static Dictionary<string, Func<object, IList<SqlParameter>>> _mappersTo = new Dictionary<string, Func<object, IList<SqlParameter>>>();

        static CmsUserInformationAllForManagementDataMapper()
        {
            _mappersTo.Add("cms_sp_GetUserForManagement",
                (p) =>
                {
                    return _based.MapTo("cms_sp_GetUserForManagement", p, false);
                });
            _mappersTo.Add("cms_sp_GetUsersForManagement",
                (p) =>
                {
                    return _based.MapTo("cms_sp_GetUsersForManagement", p, false);
                });
            _mappersTo.Add("cms_sp_GetUserAndUserGroups",
                (p) =>
                {
                    return _based.MapTo("cms_sp_GetUserAndUserGroups", p, false);
                });
            _mappersTo.Add("cms_sp_CreateUser", CreateMapperForUserCreate);
            _mappersTo.Add("cms_sp_UpdateUser", CreateMapperForUserUpdate);
            _mappersTo.Add("cms_sp_UserAssignGroups", CreateMapperForUserAssignOrRemoveGroups);
            _mappersTo.Add("cms_sp_UserRemoveGroups", CreateMapperForUserAssignOrRemoveGroups);
        }

        public CmsUserInformationAllForManagementDataMapper()
            : base()
        { }

        public override IList<SqlParameter> MapTo(string sp, object parameter, bool isForCreate)
        {
            Guard.ArgumentNotEmpty(sp, "sp");
            Guard.ArgumentNotNull(parameter, "parameter");

            return _mappersTo[sp](parameter);
        }

        public override CmsUserInformation MapFrom(string sp, SqlDataReader reader)
        {
            Guard.ArgumentNotEmpty(sp, "sp");
            Guard.ArgumentNotNull(reader, "reader");

            var result = new CmsUserInformation();

            result.TotalCount = reader.GetInt32(reader.GetOrdinal("TotalCount"));

            result.LoggedInUser = new CmsUser
            {
                Id = reader.GetInt32(reader.GetOrdinal("UserId")),
                SiteId = !reader.IsDBNull(reader.GetOrdinal("SiteId")) ?
                    reader.GetInt32(reader.GetOrdinal("SiteId")) :
                    new Nullable<int>(),
                SiteGroupId = !reader.IsDBNull(reader.GetOrdinal("SiteGroupId")) ?
                    reader.GetInt32(reader.GetOrdinal("SiteGroupId")) :
                    new Nullable<int>(),
                LoginName = reader.GetString(reader.GetOrdinal("LoginName")),
                UserEmail = reader.GetString(reader.GetOrdinal("UserEmail")),
                Firstname = reader.GetString(reader.GetOrdinal("Firstname")),
                Lastname = !reader.IsDBNull(reader.GetOrdinal("Lastname")) ? reader.GetString(reader.GetOrdinal("Lastname")) : null,
                Locale = !reader.IsDBNull(reader.GetOrdinal("Locale")) ? reader.GetString(reader.GetOrdinal("Locale")) : null,
                IsEnabled = reader.GetBoolean(reader.GetOrdinal("IsEnabled")),
                IsSystem = reader.GetBoolean(reader.GetOrdinal("IsSystem")),
                CreatedBy = reader.GetInt32(reader.GetOrdinal("CreatedBy")),
                DateCreated = reader.GetDateTime(reader.GetOrdinal("DateCreated")),
                LastUpdatedBy = !reader.IsDBNull(reader.GetOrdinal("LastUpdatedBy")) ?
                    reader.GetInt32(reader.GetOrdinal("LastUpdatedBy")) :
                    new Nullable<int>(),
                DateLastUpdated = !reader.IsDBNull(reader.GetOrdinal("DateLastUpdated")) ?
                   reader.GetDateTime(reader.GetOrdinal("DateLastUpdated")) :
                   new Nullable<DateTime>(),
                UserCreatedBy = !reader.IsDBNull(reader.GetOrdinal("UserCreatedBy")) ?
                    reader.GetString(reader.GetOrdinal("UserCreatedBy")) :
                    string.Empty,
                UserLastUpdatedBy = !reader.IsDBNull(reader.GetOrdinal("UserLastUpdatedBy")) ?
                    reader.GetString(reader.GetOrdinal("UserLastUpdatedBy")) :
                    string.Empty
            };

            return result;
        }

        public override CmsUserInformation MapFromMulti(string sp, SqlDataReader reader, CmsUserInformation stepEntity, int index)
        {
            Guard.ArgumentNotEmpty(sp, "sp");
            Guard.ArgumentNotNull(reader, "reader");

            if (stepEntity == null)
            {
                stepEntity = new CmsUserInformation();
            }

            if (index == 0)
            {
                stepEntity.LoggedInUser = new CmsUser
                {
                    Id = reader.GetInt32(reader.GetOrdinal("UserId")),
                    SiteId = !reader.IsDBNull(reader.GetOrdinal("SiteId")) ?
                        reader.GetInt32(reader.GetOrdinal("SiteId")) :
                        new Nullable<int>(),
                    SiteGroupId = !reader.IsDBNull(reader.GetOrdinal("SiteGroupId")) ?
                        reader.GetInt32(reader.GetOrdinal("SiteGroupId")) :
                        new Nullable<int>(),
                    LoginName = reader.GetString(reader.GetOrdinal("LoginName")),
                    UserEmail = reader.GetString(reader.GetOrdinal("UserEmail")),
                    Firstname = reader.GetString(reader.GetOrdinal("Firstname")),
                    Lastname = !reader.IsDBNull(reader.GetOrdinal("Lastname")) ? reader.GetString(reader.GetOrdinal("Lastname")) : null,
                    Locale = !reader.IsDBNull(reader.GetOrdinal("Locale")) ? reader.GetString(reader.GetOrdinal("Locale")) : null,
                    PasswordHash = reader.GetString(reader.GetOrdinal("PasswordHash")),
                    PasswordSalt = !reader.IsDBNull(reader.GetOrdinal("PasswordSalt")) ?
                        reader.GetString(reader.GetOrdinal("PasswordSalt")) :
                        null,
                    HasInitialPassword = reader.GetBoolean(reader.GetOrdinal("IsInitial")),
                    IsEnabled = reader.GetBoolean(reader.GetOrdinal("IsEnabled")),
                    IsSystem = reader.GetBoolean(reader.GetOrdinal("IsSystem")),
                    CreatedBy = reader.GetInt32(reader.GetOrdinal("CreatedBy")),
                    DateCreated = reader.GetDateTime(reader.GetOrdinal("DateCreated")),
                    LastUpdatedBy = !reader.IsDBNull(reader.GetOrdinal("LastUpdatedBy")) ?
                        reader.GetInt32(reader.GetOrdinal("LastUpdatedBy")) :
                        new Nullable<int>(),
                    DateLastUpdated = !reader.IsDBNull(reader.GetOrdinal("DateLastUpdated")) ?
                       reader.GetDateTime(reader.GetOrdinal("DateLastUpdated")) :
                       new Nullable<DateTime>()
                };
            }
            else if (index == 1)
            {
                CmsUserGroup stepUserGroup = new CmsUserGroup
                {
                    Id = reader.GetInt32(reader.GetOrdinal("UserGroupId")),
                    SiteId = !reader.IsDBNull(reader.GetOrdinal("SiteId")) ?
                        reader.GetInt32(reader.GetOrdinal("SiteId")) :
                        new Nullable<int>(),
                    SiteGroupId = !reader.IsDBNull(reader.GetOrdinal("SiteGroupId")) ?
                        reader.GetInt32(reader.GetOrdinal("SiteGroupId")) :
                        new Nullable<int>(),
                    GroupName = reader.GetString(reader.GetOrdinal("GroupName")),
                    IsEnabled = reader.GetBoolean(reader.GetOrdinal("IsEnabled")),
                    IsSystem = reader.GetBoolean(reader.GetOrdinal("IsSystem")),
                    CreatedBy = reader.GetInt32(reader.GetOrdinal("CreatedBy")),
                    DateCreated = reader.GetDateTime(reader.GetOrdinal("DateCreated")),
                    LastUpdatedBy = !reader.IsDBNull(reader.GetOrdinal("LastUpdatedBy")) ?
                        reader.GetInt32(reader.GetOrdinal("LastUpdatedBy")) :
                        new Nullable<int>(),
                    DateLastUpdated = !reader.IsDBNull(reader.GetOrdinal("DateLastUpdated")) ?
                       reader.GetDateTime(reader.GetOrdinal("DateLastUpdated")) :
                       new Nullable<DateTime>()
                };

                if (stepEntity.LoggedInUserGroups == null)
                {
                    stepEntity.LoggedInUserGroups = new CmsUserGroup[] { stepUserGroup };
                }
                else
                {
                    stepEntity.LoggedInUserGroups = stepEntity.LoggedInUserGroups.Concat(new CmsUserGroup[] { stepUserGroup }).ToList();
                }
            }
            else
            {
                throw new InvalidOperationException(string.Format("Stored procedure {0} can't return more than {1} result", sp, index));
            }

            return stepEntity;
        }

        private static IList<SqlParameter> CreateMapperForUserCreate(object parameter)
        {
            IList<SqlParameter> result = new List<SqlParameter>();

            var concreteParameter = parameter as CmsCreateUpdateUserCommandParameter;
            if (concreteParameter != null)
            {
                result.Add(new SqlParameter("UserId", concreteParameter.Id) { Direction = ParameterDirection.ReturnValue });

                if (concreteParameter.SiteId.HasValue)
                {
                    result.Add(new SqlParameter("SiteId", concreteParameter.SiteId.Value));
                }
                if (concreteParameter.SiteGroupId.HasValue)
                {
                    result.Add(new SqlParameter("SiteGroupId", concreteParameter.SiteGroupId.Value));
                }

                result.Add(new SqlParameter("LoginName", concreteParameter.LoginName));
                result.Add(new SqlParameter("UserEmail", concreteParameter.UserEmail));
                result.Add(new SqlParameter("Firstname", concreteParameter.Firstname));
                result.Add(new SqlParameter("Lastname", concreteParameter.Lastname));
                result.Add(new SqlParameter("PasswordHash", concreteParameter.PasswordHash));
                result.Add(new SqlParameter("PasswordSalt", concreteParameter.PasswordSalt));
                result.Add(new SqlParameter("IsSystem", concreteParameter.IsSystem));
                result.Add(new SqlParameter("CreatedBy", concreteParameter.CreatedBy));
                result.Add(new SqlParameter("DateCreated", concreteParameter.DateCreated));
                result.Add(new SqlParameter("IsInitial", concreteParameter.IsInitial));
                result.Add(new SqlParameter("Locale", concreteParameter.Locale));
            }

            return result;
        }

        private static IList<SqlParameter> CreateMapperForUserUpdate(object parameter)
        {
            IList<SqlParameter> result = new List<SqlParameter>();

            var concreteParameter = parameter as CmsCreateUpdateUserCommandParameter;
            if (concreteParameter != null)
            {
                result.Add(new SqlParameter("UserId", concreteParameter.Id));

                if (concreteParameter.SiteId.HasValue)
                {
                    result.Add(new SqlParameter("SiteId", concreteParameter.SiteId.Value));
                }
                if (concreteParameter.SiteGroupId.HasValue)
                {
                    result.Add(new SqlParameter("SiteGroupId", concreteParameter.SiteGroupId.Value));
                }

                result.Add(new SqlParameter("LoginName", concreteParameter.LoginName));
                result.Add(new SqlParameter("UserEmail", concreteParameter.UserEmail));
                result.Add(new SqlParameter("Firstname", concreteParameter.Firstname));
                result.Add(new SqlParameter("Lastname", concreteParameter.Lastname));
                if (!string.IsNullOrWhiteSpace(concreteParameter.PasswordHash))
                {
                    result.Add(new SqlParameter("PasswordHash", concreteParameter.PasswordHash));
                }
                result.Add(new SqlParameter("PasswordSalt", concreteParameter.PasswordSalt));
                result.Add(new SqlParameter("IsEnabled", concreteParameter.IsEnabled));
                result.Add(new SqlParameter("IsSystem", concreteParameter.IsSystem));
                result.Add(new SqlParameter("LastUpdatedBy", concreteParameter.LastUpdatedBy));
                result.Add(new SqlParameter("DateLastUpdated", concreteParameter.DateLastUpdated));
                result.Add(new SqlParameter("Locale", concreteParameter.Locale));
            }

            return result;
        }

        private static IList<SqlParameter> CreateMapperForUserAssignOrRemoveGroups(object parameter)
        {
            IList<SqlParameter> result = new List<SqlParameter>();

            var concreteParameter = parameter as CmsUserAssignToGroupsCommandParameter;
            if (concreteParameter != null)
            {
                result.Add(new SqlParameter("UserId", concreteParameter.UserId));
                result.Add(new SqlParameter("UserGroupIds", concreteParameter.UserGroupIds.Select(p => p.ToString()).ToPlainString()));
            }

            return result;
        }
    }
}